package com.jch.webstatic.handler;

import com.jch.webstatic.WebStaticServer;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.*;
import io.netty.handler.codec.http.*;
import io.netty.handler.ssl.SslHandler;
import io.netty.handler.stream.ChunkedNioFile;
import io.netty.util.CharsetUtil;

import java.io.File;
import java.io.RandomAccessFile;

// 静态web服务处理器
public class StaticWebHandler extends SimpleChannelInboundHandler<FullHttpRequest> {


    protected String pagePath;

    public StaticWebHandler(String pagePath) {
        this.pagePath = pagePath;
    }

    @Override
    protected void channelRead0(ChannelHandlerContext ctx, FullHttpRequest request) throws Exception {
        if (HttpUtil.is100ContinueExpected(request)) {
            System.out.println("1xx 请求.");
            FullHttpResponse response = new DefaultFullHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.CONTINUE);
            ctx.writeAndFlush(response);
        }

        // 无法解析的请求
        if (request.decoderResult().isFailure()) {
            System.out.println("请求无法解析：" + request.uri());
            FullHttpResponse response = new DefaultFullHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.BAD_REQUEST, Unpooled.copiedBuffer("请求失败。", CharsetUtil.UTF_8));
            response.headers().set(HttpHeaderNames.CONTENT_TYPE, "text/plain;charset=utf-8");
            ctx.writeAndFlush(response).addListener(ChannelFutureListener.CLOSE);
        }

        if (request.method() != HttpMethod.GET) {
            System.err.println("非GET请求：" + request.uri() + " " + request.method());
            FullHttpResponse response = new DefaultFullHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.FORBIDDEN);
            ByteBuf content = Unpooled.copiedBuffer("只允许GET请求。", CharsetUtil.UTF_8);
            // 写入response中
            response.content().writeBytes(content);
            // 释放ByteBuf
            content.release();
            // 设置response content 长度
            HttpUtil.setContentLength(response, response.content().readableBytes());

            ctx.writeAndFlush(response).addListener(ChannelFutureListener.CLOSE);
        }

        String uri = request.uri();
        System.out.println("uri: " + uri);
        System.out.println("pagePath: " + pagePath);
        uri = uri.equals("/") ? "/index.html" : uri;
        String filePath = WebStaticServer.class.getResource(pagePath).getPath() + uri;
        System.out.println(filePath);
        File pageFile = new File(filePath);

        // 文件不存在
        if (!pageFile.exists()) {
            System.err.println("资源不存在：" + request.uri());
            FullHttpResponse response = new DefaultFullHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.NOT_FOUND, Unpooled.copiedBuffer("资源不存在。", CharsetUtil.UTF_8));
            response.headers().set(HttpHeaderNames.CONTENT_TYPE, "text/plain;charset=utf-8");
            ctx.writeAndFlush(response).addListener(ChannelFutureListener.CLOSE);
        }
        // 组装正常响应信息
        HttpResponse response = new DefaultHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.OK);
        // 响应内容类型
        if (filePath.endsWith(".html")) {
            response.headers().set(HttpHeaderNames.CONTENT_TYPE, HttpHeaderValues.TEXT_HTML + ";" + CharsetUtil.UTF_8);
        } else if (filePath.endsWith(".js")) {
            response.headers().set(HttpHeaderNames.CONTENT_TYPE, "application/x-javascript");
        } else if (filePath.endsWith(".css")) {
            response.headers().set(HttpHeaderNames.CONTENT_TYPE, HttpHeaderValues.TEXT_CSS + ";" + CharsetUtil.UTF_8);
        } else if (uri.equals("/favicon.ico")) { //favicon.ico
            response.headers().set(HttpHeaderNames.CONTENT_TYPE, "image/x-icon");
        }
        // 检查是否为keepalive
        if (HttpUtil.isKeepAlive(request)) {
            response.headers().set(HttpHeaderNames.CONTENT_LENGTH, pageFile.length());
            response.headers().set(HttpHeaderNames.CONNECTION, HttpHeaderValues.KEEP_ALIVE);
        }
        // 发送响应信息
        ctx.write(response);

        // 分块传输
        RandomAccessFile raf = new RandomAccessFile(pageFile, "r");
        if (ctx.pipeline().get(SslHandler.class) == null) {
            ctx.write(new DefaultFileRegion(raf.getChannel(), 0, raf.length()));
        } else {
            ctx.write(new ChunkedNioFile(raf.getChannel()));
        }

        // 监控文件是否传输完成
        ChannelFuture channelFuture = ctx.writeAndFlush(LastHttpContent.EMPTY_LAST_CONTENT);
        if (!HttpUtil.isKeepAlive(request)) {
            channelFuture.addListener(ChannelFutureListener.CLOSE);
        }
        raf.close(); // 关闭文件读取流
    }

    @Override
    public void channelActive(ChannelHandlerContext ctx) throws Exception {
        System.out.println("收到连接：" + ctx.channel().remoteAddress().toString());
    }

    @Override
    public void channelInactive(ChannelHandlerContext ctx) throws Exception {
        System.out.println("连接：" + ctx.channel().remoteAddress().toString() + " 主动断开。");
    }

    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
        ctx.close();
        System.err.println("通道出现异常：" + cause.getMessage() + " 已关闭异常通道。");
    }
}
